﻿#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
using namespace std;

/*
//写一个Person  全价买票类
class Person
{
public:
	//买票函数
	virtual void BuyTicket()     //加上virtual就是虚函数      如果把这里的virtual去掉的话   就不满足多态 会输出两个全价
	//void BuyTicket()     这个去掉不满足多态
	{
		cout << "买票 -- 全价" << endl;
	}
	//虚函数virtual 放在返回值的前面  只能放在类的成员函数中   
};

//虚函数的重写/覆盖
class Student : public Person
{
public:
	virtual void  BuyTicket()     //这里的virtual即使去掉也不影响   因为先把父类的基函数给继承了下来
	{
		cout << "买票 -- 打折" << endl;
	}
};

void Func(Person* ptr)    //传参数基类的指针或者引用   必须是指针或者引用   且必须是基类的   如果这里是Student就只能后续传Student
{
	//这里可见得虽然都是Person指针ptr在调用BuyTicket
	//但是跟ptr没有关系   而是由ptr指向的对象决定的
	ptr->BuyTicket();     //没有上面的virtual的话   这里的ptr是谁的就调用谁的
}

int main()
{
	Person ps;
	Student st;
	Func(&ps);     //这个多态是指向谁   就调用谁    传派生类会切片
	Func(&st);
	
	return 0;
}

*/


/*

//动物叫声的多态    不同动物继承这个声音   就会有不同的声音
class Animal
{
public:
	virtual void talk() const
	{}
};

class Dog : public Animal
{
public:
	virtual void talk() const 
	{
		cout << "汪汪汪" << endl;
	}
};

class Cat :public Animal 
{
public:
	virtual void talk() const
	{
		cout << "喵喵喵" << endl;
	}
};

void letsHear(const Animal& animal)
{
	animal.talk();
}    //一个函数  不同的结果    传不同的对象不同的结果

int main()
{
	Dog D;
	Cat C;

	letsHear(D);
	letsHear(C);

	return 0;
}

*/

/*
class A
{
public:
	virtual void func(int val = 1)
	{
		cout << "A->" << val << endl;
	}

	virtual void test()
	{
		func();
	}
};

class B :public A
{
public:
	void func(int val = 0)    //B中的函数完成了A中的函数重写   即使没有加virtual
	{
		cout << "B->" << val << endl;
	}
};

int  main()
{
	B* p = new B;
	p->test();  //这里去调用了B中继承的A中的函数   相当于把B这个对象传给了A中test函数的this   但是实际this还在A类中的   故还是A*
	//无论A调用还是B调用    都是A*     this->func()   构成多态    构成了多态
	//但是答案  是 B->1
	//重写   是基类的虚函数声明和派生类的函数体   共同组成重写    本质是重写虚函数的实现   只有多态才会组合重写

	p->func();    //这个输出就是 B->0     因为不满足多态  不会重写   就是普通调用，跟父类没关系

	return 0;
}

*/

/*

//下面构成了协变   返回类型不相同   但是也构成了多态   当然也可以返回父类 & 子类     或者其他类
class A {};
class B:public A {};
class Person
{
public:
	//买票函数
	virtual A* BuyTicket()     //加上virtual就是虚函数      如果把这里的virtual去掉的话   就不满足多态 会输出两个全价
		//void BuyTicket()     这个去掉不满足多态
	{
		cout << "买票 -- 全价" << endl;
	}
	//虚函数virtual 放在返回值的前面  只能放在类的成员函数中   
};

//虚函数的重写/覆盖
class Student : public Person
{
public:
	virtual B* BuyTicket()     //这里的virtual即使去掉也不影响   因为先把父类的基函数给继承了下来
	{
		cout << "买票 -- 打折" << endl;
	}
};

void Func(Person* ptr)    //传参数基类的指针或者引用   必须是指针或者引用   且必须是基类的   如果这里是Student就只能后续传Student
{
	//这里可见得虽然都是Person指针ptr在调用BuyTicket
	//但是跟ptr没有关系   而是由ptr指向的对象决定的
	ptr->BuyTicket();     //没有上面的virtual的话   这里的ptr是谁的就调用谁的
}

int main()
{
	Person ps;
	Student st;
	Func(&ps);     //这个多态是指向谁   就调用谁    传派生类会切片
	Func(&st);

	return 0;
}

*/


/*
	为什么基类的析构函数建议设计为虚函数？
	
    下面的代码可见，如果~A(),不加virtual，那么delete  p2时只调用A的析构函数，没有调用B的析构函数，就会导致内存泄漏问题
	因为~B()中存在释放资源

	第一个 ~A是调用p1的    p2构成多态指向B   派生类对象由两部分构成   一部分子类一部分父类
	要保证先子后父    自动调用析构
	只有析构函数构成多态才能进行正常的释放   故析构函数名会被特殊处理成为destructor   这样又会构成基类派生类的隐藏
	就需要设计成为虚函数
*/

#if 0
class A
{
public:
	virtual ~A()
	{
		cout << "~A()" << endl;
	}
};

class B : public A
{
public:
	//实际上这样也构成了重写
	virtual ~B()
	{
		cout << "~B()->delete:" << _p << endl;
		delete _p;
	}
protected:
	int* _p = new int[10];
};

int main()
{
	//这是因为有一个父类的指针  父类指针可能指向父类对象也可能指向子类对象
	A* p1 = new A;
	A* p2 = new B;

	//p1 ->析构函数 + operator delete（free）
	delete p1;    //这里的析构没问题
	delete p2;    //这里期望调用子类的析构     只能构成多态   然后指向谁调用谁

/*
输出
	~A()
	~B()->delete:000001826AAC1A20
	~A()

第一个 ~A是调用p1的    p2构成多态指向B  派生类对象由两部分构成   一部分子类一部分父类
要保证先子后父   自动调用析构
只有析构函数构成多态才能进行正常的释放   故析构函数名会被特殊处理成为destructor   所以会构成基类派生类的隐藏
*/
	return 0;
}

#endif

/*

//抽象类
class Car
{
public:
	//纯虚函数
	virtual void Drive() = 0;
};

//若想让下面这个实例化出对象   需要进行重写
//class Benz :public Car
//{
//
//};

class Benz :public Car
{
public:
	virtual void Drive()
	{
		cout << "Benz - 舒服" << endl;
	}
};

class BMW :public Car
{
public:
	virtual void Drive()
	{
		cout << "BMW - 掌控" << endl;
	}
};

int main()
{
	//Car C;     //纯虚函数不能实例化出对象
	//Benz z;    //Benz  也无法实例化出对象     对应上面的继承是无法实例化出对象的

	//Benz zz;    //重写之后  就可以实例化出对象了
	

	Car* pBenz = new Benz;
	pBenz->Drive();
	
	Car* pBMW = new BMW;
	pBMW->Drive();    //父类指向子类的对应的虚函数
	
	return 0;
}

*/

/*
//C++ 计算类的大小只计算成员变量
class Base
{
public:
	virtual void Func1()    //注意  这里加上了虚函数
	{
		cout << "Func1()" << endl;
	}

	virtual void Func2()    //注意  这里加上了虚函数
	{
		cout << "Func1()" << endl;
	}

	void Func3()    //注意  这里加上了虚函数
	{
		cout << "Func1()" << endl;
	}

protected:
	int _b = 1;
	char _ch = 'x';
};

int main()
{
	Base b;
	cout << sizeof(b) << endl;    //输出：12
	return 0;
}
//除了_b和_ch成员，还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面，这个跟平台有关)，
//对象中的这个指针我们叫做虚函数表指针(v代表virtual，f代表function)。函数指针数组
//一个含有虚函数的类中都至少都有一个虚函数表指针，因为一个类所有虚函数的地址要被放到这个类对象的虚函数表中，虚函数表也简称虚表。
//是一个数组 --- 指针数组（存放指针） --- 函数指针数组

//注：上面的规定只有是虚函数   才会放进去   会把虚函数的指针都放进去

*/


/*
//几个对象就有几个虚表
class Person
{
public:
	virtual void BuyTicket() { cout << "买票 - 全价" << endl; }
protected:
	string _name;
};

class Student :public Person
{
public:
	virtual void BuyTicket() { cout << "买票 - 半价" << endl; }
protected:
	int _id;
};

class Soldier : public Person
{
public :
	virtual void BuyTicket() { cout << "买票 - 优先" << endl; }
protected:
	string _codename;
};

void Func(Person* ptr)
{
	ptr->BuyTicket();   //ptr无论如何看到的都是Person对象（可能是切片切出来的   可能是原Person）
}

//达到多态的特点的效果：指向谁调用谁
int main()
{
	Person per;
	Student stu;
	Soldier sold;

	Func(&per);
	Func(&stu);
	Func(&sold);

	return 0;
}
//指向哪个对象，运行时，到指向对象的虚函数表中找到对应虚函数的地址，进行调用
//指向父类找到的是父类的虚函数    指向子类找到的是子类的虚函数       传的是不同对象父类子类的虚表不同   

*/


class Base 
{
public:
	virtual void func1() { cout << "Base::func1" << endl; }
	virtual void func2() { cout << "Base::func2" << endl; }
	void func5() { cout << "Base::func5" << endl; }
protected:
	int a = 1;
};

class Derive : public Base
{
public:
	// 重写基类的func1 
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func1" << endl; }
	void func4() { cout << "Derive::func4" << endl; }
protected:
	int b = 2;
};

int main()
{
	int i = 0;
	static int j = 1;
	int* p1 = new int;
	const char* p2 = "xxxxx";
	printf("栈：%p\n", &i);
	printf("静态区：%p\n", &j);
	printf("堆：%p\n", p1);
	printf("常量区：%p\n", p2);

	Base b;
	Derive d;
	Base* p3 = &b;
	Derive* p4 = &d;
	printf("Person虚表地址:%p\n", *(int*)p3);    //强转为int*   解引用就只看最上面的四字节
	printf("Student虚表地址:%p\n", *(int*)p4);
	printf("虚函数地址:%p\n", &Base::func1);
	printf("普通函数地址:%p\n", &Base::func5);

	return 0;
}

